/*
 * HMM.cpp
 *
 *  Created on: 03.11.2011
 *	  Author: pavel
 */

#include "HMM.h"
#include "algs/Algorithms.h"
#include "algs/TrainAlgs.h"

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <math.h>

HMM::HMM() {
	eps = 0;
	delta = 0.001;
	maxIter = 100;
}

HMM::HMM(double eps) {
	this->eps = eps;
	delta = 0.001;
	maxIter = 100;
}


HMM::HMM(unsigned n, unsigned m,
	const std::vector<double> &pi,
	const std::vector<std::vector<double> > &a,
	const std::vector<std::vector<double> > &b):
	n(n), m(m), pi(pi), a(a), b(b) {
	eps = 0;
	delta = 0.001;
	maxIter = 100;

}

HMM::HMM(unsigned n, unsigned m) : n(n), m(m) {
	initEvenly(n, m);
	eps = 0;
	delta = 0.001;
	maxIter = 100;
}

HMM::~HMM() {
	// TODO Auto-generated destructor stub
}

void HMM::setNumberOfStates(unsigned n) {
	this->n = n;
	pi.resize(n);
	a.resize(n);
	b.resize(n);
	for (size_t i = 0; i < n; i++) {
		a[i].resize(n);
		b[i].resize(m);
	}
}

void HMM::setAlphabetSize(unsigned m) {
	this->m = m;
	for (size_t i = 0; i < n; i++) {
		b[i].resize(m);
	}
}


unsigned HMM::getNumberOfStates() const {
	return n;
}

unsigned HMM::getAlphabetSize() const {
	return m;
}

void HMM::setEps(double eps) {
	this->eps = eps;
}

double HMM::getEps() {
	return this->eps;
}


double HMM::piAt(unsigned i) const {
	return pi[i];
}
double HMM::aAt(unsigned i, unsigned j) const {
	return a[i][j];
}
double HMM::bAt(unsigned i, unsigned j) const {
	return b[i][j];
}


void HMM::init(unsigned n, unsigned m) {
	this->n = n;
	this->m = m;
	pi.resize(n);
	a.resize(n);
	b.resize(n);
	for (size_t i = 0; i < n; i++) {
		a[i].resize(n);
		b[i].resize(m);
	}
}


void HMM::initRandom(unsigned n, unsigned m) {
	this->n = n;
	this->m = m;
	pi.resize(n);
	a.resize(n);
	b.resize(n);
	for (size_t i = 0; i < n; i++) {
		pi[i] = HMMALG::random();
		
		a[i].resize(n);
		for (size_t j = 0; j < n; j++) {
			a[i][j] = HMMALG::random();
		}
		HMMALG::normalize(a[i]);
		
		b[i].resize(m);
		for (size_t j = 0; j < m; j++) {
			b[i][j] = HMMALG::random();
		}
		HMMALG::normalize(b[i]);
	}
	HMMALG::normalize(pi);
}

void HMM::initEvenly(unsigned n, unsigned m) {
	this->n = n;
	this->m = m;
	pi.resize(n);
	a.resize(n);
	b.resize(n);
	for (size_t i = 0; i < n; i++) {
		pi[i] = 1;
		
		a[i].resize(n);
		for (size_t j = 0; j < n; j++) {
			a[i][j] = 1;
		}
		HMMALG::normalize(a[i]);
		
		b[i].resize(m);
		for (size_t j = 0; j < m; j++) {
			b[i][j] = 1;
		}
		HMMALG::normalize(b[i]);
	}
	HMMALG::normalize(pi);
}

void HMM::initLRB(unsigned n, unsigned m, double p) {
	init(n, m);
	for (size_t i = 0; i < n; i++) {
		pi[i] = 0;
		
		for (size_t j = 0; j < n; j++) {
			a[i][j] = 0;
		}
		if (i < n - 1) {
			a[i][i] = p;
			a[i][i + 1] = 1 - p;
		} else {
			a[i][i] = 1;
		}
		
		for (size_t j = 0; j < m; j++) {
			b[i][j] = 1;
		}
		HMMALG::normalize(b[i]);
	}
	pi[0] = 1;
}


void HMM::train(const Observation &obs) {
	HMMALG::trainModel(obs, n, m, pi, a, b, maxIter, delta, eps);
}

void HMM::train(const std::vector<Observation> &obs) {
	HMMALG::trainModel(obs, n, m, pi, a, b, maxIter, delta, eps);
}


void HMM::generate(Observation &obs) const {
	HMMALG::generate(obs, n, m, pi, a, b);
}

void HMM::train(const HMM &source, unsigned tmax) {
	Observation obs(tmax);
	source.generate(obs);
	train(obs);
}

double HMM::logProb(const Observation &obs) const {
	return HMMALG::logProbOfObservationOnModel(obs, n, m, pi, a, b);
}

void HMM::findMostProbablePath(const Observation &obs,
							   std::vector<unsigned> &path) const {
	HMMALG::findMostProbablePath(obs, n, m, pi, a, b, path);
}

void HMM::print() {
	std::cout << "HMM(n = " << n << ", m = " << m << ")\n";
	printf("pi\n");
	HMMALG::printVector(pi);
	printf("a\n");
	HMMALG::printMatrix(a);
	printf("b\n");
	HMMALG::printMatrix(b);
}
